#include "main.h"


/********************************************
@功能: 引脚GPIO配置
@日期：2022-08-06 09:32
@作者：lengyuefeng
@说明: 
@参数: 无
@返回值: 无
*********************************************/
static void W25QXX_GpioConfig(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(W25QXX_CS_RCC, ENABLE); //使能时钟

	GPIO_InitStructure.GPIO_Pin 	= 	W25QXX_CS_PIN;
	GPIO_InitStructure.GPIO_Speed 	= 	GPIO_Speed_50MHz; 		//速度50MHz
	GPIO_InitStructure.GPIO_Mode 	= 	GPIO_Mode_Out_PP;	 	//输出模式
	GPIO_Init(W25QXX_CS_PORT, &GPIO_InitStructure); 	//初始化
}

/********************************************
@功能: W25Q初始化函数 
@日期：2022-08-06 09:47
@作者：lengyuefeng
@说明: 
@参数: 无
@返回值: 无
*********************************************/
void W25QXX_Init(void)
{
    W25QXX_GpioConfig();
}

/********************************************
@功能: 写使能 
@日期：2022-08-06 09:32
@作者：lengyuefeng
@说明: 
@参数: 无
@返回值: 无
*********************************************/
void W25QXX_WriteEnable(void)
{
    W25QXX_CS_WRITE(0);                 //拉低选中芯片
	W25QXX_READ_WRITE_BYTE(W25QXX_WRITE_ENABLE);       //写使能
	W25QXX_CS_WRITE(1);                 //拉高不选中芯片
}

/********************************************
@功能: 禁止写 
@日期：2022-08-06 09:32
@作者：lengyuefeng
@说明: 
@参数: 无
@返回值: 无
*********************************************/
void W25QXX_WriteDisable(void)
{
    W25QXX_CS_WRITE(0);                 //拉低选中芯片
	W25QXX_READ_WRITE_BYTE(W25QXX_WRITE_DISABLE);       //禁止使能
	W25QXX_CS_WRITE(1);                 //拉高不选中芯片
}

/********************************************
@功能: 读取状态寄存器/等待芯片非忙碌状态
@日期：2022-08-06 09:32
@作者：lengyuefeng
@说明: 
@参数: 无
@返回值: 无
*********************************************/
void W25QXX_ReadStatus()
{
	uint8_t statue = 0;//状态寄存器值变量
    W25QXX_CS_WRITE(0); //拉低选中芯片
	W25QXX_READ_WRITE_BYTE(W25QXX_READ_STATUS_REGISTER);//读状态寄存器指令
	do
	{
		statue = W25QXX_READ_WRITE_BYTE(W25QXX_DUMMY_BYTE);//读取状态
	}
	while(statue & 0x01);//等待芯片非忙绿状态
    W25QXX_CS_WRITE(1);   //拉高不选中芯片		
}

/********************************************
@功能: 写状态寄存器
@日期：2022-08-06 09:32
@作者：lengyuefeng
@说明: 
@参数: 无
@返回值: 无
*********************************************/
void W25QXX_WriteStatus(uint8_t byte)
{
    W25QXX_CS_WRITE(0); //拉低选中芯片
    W25QXX_WriteEnable();// 使能写
	W25QXX_READ_WRITE_BYTE(W25QXX_WRITE_STATUS_REGISTER);//写状态寄存器指令
    W25QXX_READ_WRITE_BYTE(byte);//写入状态的数据
    W25QXX_CS_WRITE(1);   //拉高不选中芯片		
}

/********************************************
@功能: 读数据 
@日期：2022-08-06 09:59
@作者：lengyuefeng
@说明: *pBuffer：读取数据存放地址
        readAddr：读取的首地址
        numRead：读取数据个数
@参数: 无
@返回值: 无
*********************************************/
void W25QXX_ReadData(uint8_t *pBuffer,uint32_t readAddr,uint16_t numRead)
{
    W25QXX_CS_WRITE(0); //拉低选中芯片
	W25QXX_READ_WRITE_BYTE(W25QXX_READ_DATA);//读取数据指令
    //24位地址
	W25QXX_READ_WRITE_BYTE((readAddr&0xff0000)>>16);//先发送高8位
	W25QXX_READ_WRITE_BYTE((readAddr&0xff00)>>8);//再发送中间8位
	W25QXX_READ_WRITE_BYTE(readAddr&0xff);//低8位
	while(numRead--)
	{
	    *pBuffer = W25QXX_READ_WRITE_BYTE(W25QXX_DUMMY_BYTE);//读一个字节的数据
	    pBuffer++;//指向下一个要读取的地址
	}
    W25QXX_CS_WRITE(1);   //拉高不选中芯片		
}

/********************************************
@功能: 快速读数据 
@日期：2022-08-06 09:59
@作者：lengyuefeng
@说明: *pBuffer：读取数据存放地址
        readAddr：读取的首地址
        numRead：读取数据个数
@参数: 无
@返回值: 无
*********************************************/
void W25QXX_FastReadData(uint8_t *pBuffer,uint32_t readAddr,uint16_t numRead)
{
    W25QXX_CS_WRITE(0); //拉低选中芯片
	W25QXX_READ_WRITE_BYTE(W25QXX_FAST_READ_DATA);//快速读取数据指令
    //24位地址
	W25QXX_READ_WRITE_BYTE((readAddr&0xff0000)>>16);//先发送高8位
	W25QXX_READ_WRITE_BYTE((readAddr&0xff00)>>8);//再发送中间8位
	W25QXX_READ_WRITE_BYTE(readAddr&0xff);//低8位

    W25QXX_READ_WRITE_BYTE(W25QXX_DUMMY_BYTE); //等待8个时钟
	while(numRead--)
	{
	    *pBuffer = W25QXX_READ_WRITE_BYTE(W25QXX_DUMMY_BYTE);//读一个字节的数据
	    pBuffer++;//指向下一个要读取的地址
	}
    W25QXX_CS_WRITE(1);   //拉高不选中芯片		
}

/********************************************
@功能: 页编程数据 
@日期：2022-08-06 10:08
@作者：lengyuefeng
@说明: *pBuffer：写入数据存放地址
        writeAddr：要写入的首地址
        numWrite：写入的数据个数
@参数: 无
@返回值: 无
*********************************************/
void W25QXX_WritePage(uint8_t *pBuffer,uint32_t writeAddr,uint16_t numWrite)
{
    W25QXX_WriteEnable();
    W25QXX_CS_WRITE(0); //拉低选中芯片
	W25QXX_READ_WRITE_BYTE(W25QXX_PAGE_PROGRAM); //页编程命令
    //24位地址
	W25QXX_READ_WRITE_BYTE((writeAddr&0xff0000)>>16);//先发送高8位
	W25QXX_READ_WRITE_BYTE((writeAddr&0xff00)>>8);//再发送中间8位
	W25QXX_READ_WRITE_BYTE(writeAddr&0xff);//低8位

    /* 写入数据大小不能超出一页 */
    if(numWrite > 256)  
    {
        numWrite = 256;
#if W25QXX_DEBUG
        W25QXX_ERROR_PRINTF("Err: W25QXX_WritePage numWrite too large,> 256\r\n");
#endif
    }

	while(numWrite--)
	{
	    W25QXX_READ_WRITE_BYTE(*pBuffer); // 写入数据
	    pBuffer++;//指向下一个要读取的地址
	}
    W25QXX_CS_WRITE(1);   //拉高不选中芯片	
    W25QXX_ReadStatus(); // 等待写操作结束	
}



////页地址不对齐
//void SPI_Flash_Write_Addr(uchar *pBuffer,ulong WriteAddr,uint NumWrite)
//{
// uchar Numofpage=0,Numofsingle=0,addr=0,count=0,time=0;//剩下的
//	addr=WriteAddr%256;//判断要写入的地址是否页对齐
//	count=256-addr;//剩下的地址
//	Numofpage=NumWrite/256;//一共写多少整页
//	Numofsingle=NumWrite%256;//还剩多少字节
//	
//	if(addr==0)//判断写入的地址是否页对齐
//	{
//	  if(Numofpage===0)//如果只需写一页
//		{
//		  SPI_Flash_Write_Page(pBuffer,WriteAddr,NumWrite);
//		}
//		else//不止写一页
//		{
//		  while(Numofpage--)
//			{
//			  SPI_Flash_Write_Page(pBuffer,WriteAddr,256);
//				WriteAddr+=256;//换下一页
//				pBuffer+=256;
//			}
//			SPI_Flash_Write_Page(pBuffer,WriteAddr,Numofsingle);//把剩下的字节写完
//		}
//	else //要写入的地址不是页对齐的地址
//	{
//	  if(Numofpage==0)//只需要写一页
//		{
//		  if(count<Numofsingle)//判断要剩下的地址如果不够写字节
//			{
//			 temp=Numofsingle-count;//还剩下的字节
//			SPI_Flash_Write_Page(pBuffer,WriteAddr,count);//写满一页
//				WriteAddr+=count;//换下一页
//				pBuffer+=count;
//				SPI_Flash_Write_Page(pBuffer,WriteAddr,temp);//往新的一页里写剩下的数据
//			}
//			else//剩下的地址够写字节
//			{
//			 SPI_Flash_Write_Page(pBuffer,WriteAddr,NumWrite);
//			}	
//		}
//		else//不止写一页
//		{
//		  NumWrite-=count;//先把空的地址填满之后还剩的字节
//			Numofpage=NumWrite/256;//总共还要写多少页
//			Numofsingle=NumWrite%256;//还剩下的字节数
//			SPI_Flash_Write_Page(pBuffer,WriteAddr,count);//把空的地址填满
//			WriteAddr+=count;//换下一页
//				pBuffer+=count;
//			while(Numofpage--)//整页
//			{
//			  SPI_Flash_Write_Page(pBuffer,WriteAddr,256);
//				WriteAddr+=256;//换下一页
//				pBuffer+=256;
//			}
//			if(Numofsingle!=0)
//			{
//			  SPI_Flash_Write_Page(pBuffer,WriteAddr,Numofsingle);//把剩下的写完
//			}
//		}	
//	 }
//	}
//}


/********************************************
@功能: 块擦除指令（64KB） 
@日期：2022-08-06 10:08
@作者：lengyuefeng
@说明: blockAddr：要擦除的块地址 
@参数: 无
@返回值: 无
*********************************************/
void W25QXX_BlockErase(uint32_t blockAddr)
{
    W25QXX_WriteEnable();
    W25QXX_CS_WRITE(0); //拉低选中芯片
	W25QXX_READ_WRITE_BYTE(W25QXX_BLOCK_ERASE_64K); //块擦除命令 
    //24位地址
	W25QXX_READ_WRITE_BYTE((blockAddr&0xff0000)>>16);//先发送高8位
	W25QXX_READ_WRITE_BYTE((blockAddr&0xff00)>>8);//再发送中间8位
	W25QXX_READ_WRITE_BYTE(blockAddr&0xff);//低8位

    W25QXX_CS_WRITE(1);   //拉高不选中芯片	
    W25QXX_ReadStatus(); // 等待擦除操作结束	
}


/********************************************
@功能: 扇区擦除指令（4KB） 
@日期：2022-08-06 10:08
@作者：lengyuefeng
@说明: sectorAddr:要擦除的扇区地址 
@参数: 无
@返回值: 无
*********************************************/
void W25QXX_SectorErase(uint32_t sectorAddr)
{
    W25QXX_WriteEnable();
    W25QXX_CS_WRITE(0); //拉低选中芯片
	W25QXX_READ_WRITE_BYTE(W25QXX_SECTOR_ERASE); //扇区擦除命令 
    //24位地址
	W25QXX_READ_WRITE_BYTE((sectorAddr&0xff0000)>>16);//先发送高8位
	W25QXX_READ_WRITE_BYTE((sectorAddr&0xff00)>>8);//再发送中间8位
	W25QXX_READ_WRITE_BYTE(sectorAddr&0xff);//低8位

    W25QXX_CS_WRITE(1);   //拉高不选中芯片	
    W25QXX_ReadStatus(); // 等待擦除操作结束	
}


/********************************************
@功能: 芯片擦除指令 
@日期：2022-08-06 10:08
@作者：lengyuefeng
@说明: 无
@参数: 无
@返回值: 无
*********************************************/
void W25QXX_ChipErase(void)
{
    W25QXX_WriteEnable();
    W25QXX_CS_WRITE(0); //拉低选中芯片
	W25QXX_READ_WRITE_BYTE(W25QXX_CHIP_ERASE); // 芯片擦除命令 
    W25QXX_CS_WRITE(1);   //拉高不选中芯片	
    W25QXX_ReadStatus(); // 等待擦除操作结束	
}

/********************************************
@功能: 芯片掉电
@日期：2022-08-06 10:19
@作者：lengyuefeng
@说明: 无
@参数: 无
@返回值: 无
*********************************************/
void W25QXX_PowerDown(void)
{
    W25QXX_CS_WRITE(0); //拉低选中芯片
	W25QXX_READ_WRITE_BYTE(W25QXX_POWER_DOWN); // 芯片掉电指令 
    W25QXX_CS_WRITE(1);   //拉高不选中芯片	
}

/********************************************
@功能: JEDEC ID序列
@日期：2022-08-06 10:19
@作者：lengyuefeng
@说明: 无
@参数: 无
@返回值: 无
*********************************************/
uint32_t W25QXX_JedecId(void)
{
    uint32_t temp=0,temp1=0,temp2=0,temp3=0;//Flash暂存变量
    W25QXX_CS_WRITE(0); //拉低选中芯片
	W25QXX_READ_WRITE_BYTE(W25QXX_JEDEC_ID); // JEDEC ID指令 
    temp1 = W25QXX_READ_WRITE_BYTE(W25QXX_DUMMY_BYTE);//从flash读取第一个字节数据
    temp2 = W25QXX_READ_WRITE_BYTE(W25QXX_DUMMY_BYTE);//从flash读取第一个字节数据
    temp3 = W25QXX_READ_WRITE_BYTE(W25QXX_DUMMY_BYTE);//从flash读取第一个字节数据
    W25QXX_CS_WRITE(1);   //拉高不选中芯片	

    temp=(((temp1<<16)|temp2<<8)|temp3);//拼合数据组成ID序列
    return temp;//返回序列号
}

